﻿using System;
using System.Collections.Generic;

namespace FirstFloor.Documents.Pdf
{
    /// <summary>
    /// Provides PDF lexer extension methods.
    /// </summary>
    internal static class PdfLexerExtensions
    {
        /// <summary>
        /// Consumes the next dictionary object from the lexer.
        /// </summary>
        /// <param name="lexer">The lexer.</param>
        /// <param name="owner">The owner.</param>
        /// <returns></returns>
        public static DictionaryObject ReadDictionaryObject(this PdfLexer lexer, PdfParser owner)
        {
            
            return lexer.ReadDictionaryObject(owner, true);
        }

        /// <summary>
        /// Consumes the next object from the lexer.
        /// </summary>
        /// <param name="lexer">The lexer.</param>
        /// <param name="owner">The owner.</param>
        /// <returns></returns>
        public static object ReadObject(this PdfLexer lexer, PdfParser owner)
        {
            var token = lexer.Read();
            if (token.Type == TokenType.ArrayStart) {
                return lexer.ReadArrayObject(owner);
            }
            else if (token.Type == TokenType.DictionaryStart) {
                return lexer.ReadDictionaryObject(owner, false);
            }
            else if (token.Type == TokenType.HexadecimalString) {
                return token.ParseHexString();
            }
            else if (token.Type == TokenType.Number) {
                // read object reference
                if (lexer.Peek().Type == TokenType.Number && lexer.Peek(1).IsMatch(TokenType.Keyword, "R")) {
                    var number = token.ParseInteger();
                    var generation = lexer.Read().ParseInteger();

                    lexer.Read();

                    return new ObjectIdentifier(number, generation);
                }

                return token.ParseDouble();
            }
            else if (token.Type == TokenType.Keyword) {
                return token.Value;
            }
            else if (token.Type == TokenType.Name) {
                return token.Value;
            }
            else if (token.Type == TokenType.String) {
                return token.Value;
            }

            throw new PdfParseException(Resources.UnexpectedToken, token);
        }

        private static DictionaryObject ReadDictionaryObject(this PdfLexer lexer, PdfParser owner, bool readStartToken)
        {
            if (readStartToken) {
                lexer.Read(TokenType.DictionaryStart);
            }

            var dictionary = new Dictionary<string, object>();

            while (true) {
                var token = lexer.Peek();
                if (token.Type == TokenType.DictionaryEnd) {
                    lexer.Read();
                    break;
                }

                var name = lexer.Read(TokenType.Name).Value;
                dictionary[name] = lexer.ReadObject(owner);
            }

            return new DictionaryObject(owner, dictionary, lexer.Stream.Position);
        }

        private static ArrayObject ReadArrayObject(this PdfLexer lexer, PdfParser owner)
        {
            var result = new ArrayObject(owner);

            while (true) {
                var token = lexer.Peek();
                if (token.Type == TokenType.ArrayEnd) {
                    lexer.Read();
                    break;
                }
                result.Add(lexer.ReadObject(owner));
            }

            return result;
        }
    }
}
